package com.devs.squaremenu;

import ohos.agp.animation.Animator;
import ohos.agp.animation.AnimatorGroup;
import ohos.agp.animation.AnimatorValue;
import ohos.agp.components.AttrSet;
import ohos.agp.components.Component;
import ohos.agp.components.element.Element;
import ohos.agp.components.element.PixelMapElement;
import ohos.agp.render.BlurDrawLooper;
import ohos.agp.render.Canvas;
import ohos.agp.render.Paint;
import ohos.agp.render.PixelMapHolder;
import ohos.agp.utils.Color;
import ohos.app.Context;
import ohos.media.image.PixelMap;
import ohos.media.image.common.PixelFormat;
import ohos.media.image.common.Size;
import ohos.multimodalinput.event.MmiPoint;
import ohos.multimodalinput.event.TouchEvent;

public class SquareMenu extends Component implements Component.LayoutRefreshedListener, Component.DrawTask,Component.TouchEventListener {

    // Default values
    private static final int FAB_MARGIN = 8;
    private static final int FAB_ICON_PADDING = 20;
    private static final float FAB_ICON_WIDTH = 2;
    private static final int MARGIN_BW_SQUARES = 3;
    private static final int FAB_SIZE = 56;
    private static final Color FAB_COLOR =Color.RED;;
    private static final long ANIM_SPEED = 200;
    private static final String TOP_LEFT = "top_left";
    private static final String TOP_RIGHT = "top_right";
    private static final String DEFAULT_DIRECTION = TOP_LEFT;

    // Custom values
    private int fabWidth = FAB_SIZE, fabHeight = FAB_SIZE;
    private Color fabColor = FAB_COLOR;
    private boolean autoClose = false;
    private String menuOpenDirection = DEFAULT_DIRECTION;
    private Paint paintFAB, paintFabBG, paintFabBGWidShadow, paintFabBGWithoutShadow, paintFabPlus, paintIcons;

    private PixelMap bitmapM1 = null, bitmapM2 = null, bitmapM3 = null;

    private int xTransTL = 0, yTransTL = 0;
    private int xTransTR = 0, yTransTR = 0;
    private int xTransBL = 0, yTransBL = 0;
    private int xTransBR = 0, yTransBR = 0;
    private float factorScaleS = 1f, factorScaleP = 0;
    private int plusRotationDegree = 0;
    private int alphaAmount = 0;
    private ValueAnimator scaleFAB, rotatePlus, alphaIcon;

    private boolean isOpened = false;
    private OnMenuClickListener onMenuClickListener = null;

    public SquareMenu(Context context) {
        super(context);
    }

    public SquareMenu(Context context, AttrSet attrSet) {
        super(context, attrSet);
        setupAttributes(attrSet);
        setupPaint();
        setLayoutRefreshedListener(this::onRefreshed);
        addDrawTask(this::onDraw);
        setTouchEventListener(this::onTouchEvent);
    }

    public SquareMenu(Context context, AttrSet attrSet, String styleName) {
        super(context, attrSet, styleName);
    }

    public SquareMenu(Context context, AttrSet attrSet, int resId) {
        super(context, attrSet, resId);
    }

    private void setupAttributes(AttrSet attrSet) {

        Element iconM1,iconM2,iconM3;

        autoClose = attrSet.getAttr("autoClose").isPresent() ?
                attrSet.getAttr("autoClose").get().getBoolValue() : false;

        fabColor = attrSet.getAttr("fabColor").isPresent() ?
                attrSet.getAttr("fabColor").get().getColorValue():Color.RED;

        fabWidth=fabHeight = attrSet.getAttr("squareFabSize").isPresent() ?
                attrSet.getAttr("squareFabSize").get().getIntegerValue():80;

        menuOpenDirection = attrSet.getAttr("menuOpenDirection").isPresent() ?
                attrSet.getAttr("menuOpenDirection").get().getStringValue():DEFAULT_DIRECTION;

        iconM1 = attrSet.getAttr("iconM1").isPresent() ?
                attrSet.getAttr("iconM1").get().getElement():getBackgroundElement();

        iconM2 = attrSet.getAttr("iconM2").isPresent() ?
                attrSet.getAttr("iconM2").get().getElement():getBackgroundElement();

        iconM3 = attrSet.getAttr("iconM3").isPresent() ?
                attrSet.getAttr("iconM3").get().getElement():getBackgroundElement();

        if(iconM1!=null)bitmapM1 = ((PixelMapElement) iconM1).getPixelMap();
        if(iconM2!=null)bitmapM2 = ((PixelMapElement) iconM2).getPixelMap();
        if(iconM3!=null)bitmapM3 = ((PixelMapElement) iconM3).getPixelMap();

    }

    private void setupPaint() {
        paintIcons = new Paint();
        paintIcons.setAlpha(0);

        paintFAB = new Paint();
        paintFAB.setStyle(Paint.Style.FILL_STYLE);
        paintFAB.setColor(fabColor);
        BlurDrawLooper blurDrawLooper = new BlurDrawLooper(0,0,0,Color.BLACK);
        paintFAB.setBlurDrawLooper(blurDrawLooper);
        // Important for certain APIs
        paintFabBGWidShadow = new Paint();
        paintFabBGWidShadow.setStyle(Paint.Style.FILL_STYLE);
        paintFabBGWidShadow.setColor(fabColor);
        paintFabBGWidShadow.setColor(Color.BLACK);
        BlurDrawLooper blurDrawLooper1 = new BlurDrawLooper(5,1,1,Color.BLACK);
        paintFabBGWidShadow.setBlurDrawLooper(blurDrawLooper1);

        paintFabBGWithoutShadow = new Paint();
        paintFabBGWithoutShadow.setStyle(Paint.Style.FILL_STYLE);
        paintFabBGWithoutShadow.setColor(Color.WHITE);

        paintFabBG = paintFabBGWidShadow;

        paintFabPlus = new Paint();
        paintFabPlus.setColor(Color.WHITE);
        paintFabPlus.setAntiAlias(true);
        paintFabPlus.setStrokeWidth(FAB_ICON_WIDTH);
    }


    @Override
    public void onRefreshed(Component component) {
         onMeasure(component.getWidth(),component.getHeight());
    }

    public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        // Defines the extra padding for the shape name text

        // Resolve the width based on our minimum and the measure spec
        int minw = 2 * fabWidth + getPaddingLeft() + getPaddingRight() + FAB_MARGIN;

        // Ask for a height that would let the view get as big as it can
        int minh = 2 * fabHeight + getPaddingBottom() + getPaddingTop() + FAB_MARGIN;

        // Calling this method determines the measured width and height
        // Retrieve with getMeasuredWidth or getMeasuredHeight methods later
        setComponentSize(minw, minh);
    }

    @Override
    public void onDraw(Component component, Canvas canvas) {
        if(menuOpenDirection.equals(TOP_LEFT))
            drawSquares4TopLeftMenu(canvas);
        else
            drawSquares4TopRightMenu(canvas);
    }

    private void drawSquares4TopRightMenu(Canvas canvas){
        // Draw BG Rect
        canvas.drawRect(0, fabHeight, fabWidth, getHeight()-FAB_MARGIN, paintFabBG);

        canvas.save();
        canvas.translate(xTransTR, yTransTR); //-(fabWidth/6-factorScaleP)
        canvas.scale(factorScaleS, factorScaleS, fabWidth/2 , fabHeight+factorScaleP);
        // Top Right Rect
        canvas.drawRect(fabWidth/2, fabHeight, fabWidth, fabHeight*1.5f, paintFAB);
        canvas.restore();

        canvas.save();
        canvas.translate(xTransTL, yTransTL);
        canvas.scale(factorScaleS, factorScaleS, 0, fabHeight+factorScaleP);

        // Top Left Rect
        canvas.drawRect(0, fabHeight, fabWidth/2, fabHeight*1.5f, paintFAB);
        canvas.restore();
        canvas.save();
        canvas.translate(xTransBR, yTransBR);
        canvas.scale(factorScaleS, factorScaleS, fabWidth/2, fabHeight*1.5f+factorScaleP);

        // Bottom Right Rect
        canvas.drawRect(fabWidth/2, fabHeight*1.5f, fabWidth, getHeight()-FAB_MARGIN, paintFAB);
        canvas.restore();
        canvas.save();

        canvas.scale(factorScaleS, factorScaleS, 0, fabHeight*1.5f+factorScaleP);

        // Bottom left Rect
        canvas.drawRect(0, fabHeight*1.5f, fabWidth/2, getHeight()-FAB_MARGIN, paintFAB);
        canvas.restore();

        // Draw PLUS
        drawPlusIcon(canvas,  FAB_ICON_PADDING, fabHeight*1.5f,  fabWidth-(2*FAB_ICON_PADDING), plusRotationDegree);

        // Draw Menus icon
        if( isOpened ) drawTopRightMenuIcons(canvas);
    }

    private void drawSquares4TopLeftMenu(Canvas canvas){
        // Draw BG Rect
        canvas.drawRect(fabWidth, fabHeight, getWidth()-FAB_MARGIN, getHeight()-FAB_MARGIN, paintFabBG);

        canvas.save();
        canvas.translate(xTransTL, yTransTL);
        canvas.scale(factorScaleS, factorScaleS, fabWidth+factorScaleP, fabHeight+factorScaleP);
        // Top Left Rect
        canvas.drawRect(fabWidth, fabHeight, fabWidth*1.5f, fabHeight*1.5f, paintFAB);
        canvas.restore();

        canvas.save();
        canvas.translate(xTransTR, yTransTR);
        canvas.scale(factorScaleS, factorScaleS, fabWidth*1.5f+factorScaleP, fabHeight+factorScaleP);
        // Top Right Rect
        canvas.drawRect(fabWidth*1.5f, fabHeight, getWidth()-FAB_MARGIN, fabHeight*1.5f, paintFAB);
        canvas.restore();

        canvas.save();
        canvas.translate(xTransBL, yTransBL);
        canvas.scale(factorScaleS, factorScaleS, fabWidth+factorScaleP, fabHeight*1.5f+factorScaleP);
        // Bottom Left Rect
        canvas.drawRect(fabWidth, fabHeight*1.5f, fabWidth*1.5f, getHeight()-FAB_MARGIN, paintFAB);
        canvas.restore();

        canvas.save();
        canvas.scale(factorScaleS, factorScaleS, fabWidth*1.5f+factorScaleP, fabHeight*1.5f+factorScaleP);
        // Bottom Right Rect
        canvas.drawRect(fabWidth*1.5f, fabHeight*1.5f, getWidth()-FAB_MARGIN, getHeight()-FAB_MARGIN, paintFAB);
        canvas.restore();

        // Draw PLUS
        drawPlusIcon(canvas, fabWidth+FAB_ICON_PADDING, fabHeight*1.5f, fabWidth-(2*FAB_ICON_PADDING), plusRotationDegree);

        // Draw Menus icon
        if( isOpened ) drawTopLeftMenuIcons(canvas);
    }

    private void drawPlusIcon(Canvas canvas, float startX, float startY, int size, int rotateDegree) {
        canvas.save();
        canvas.rotate(rotateDegree, startX + size / 2, startY);
        canvas.drawLine(startX, startY, startX + size, startY, paintFabPlus);
        canvas.rotate(90, startX + size / 2, startY);
        canvas.drawLine(startX, startY, startX + size, startY, paintFabPlus);
        canvas.restore();
    }

    private void drawTopLeftMenuIcons(Canvas canvas){
        int imageStartX,imageStartY;
        if(bitmapM1 !=null) {
            imageStartX = (0 + (fabWidth/2)) - (bitmapM1.getImageInfo().size.width);
            imageStartY = (fabHeight + (fabWidth/2)) - (bitmapM1.getImageInfo().size.height);
            PixelMap.InitializationOptions opts = new PixelMap.InitializationOptions();
            opts.size = new Size(40,40);
            opts.pixelFormat = PixelFormat.ARGB_8888;
            PixelMap pixelMap = PixelMap.create(bitmapM1,opts);
            canvas.drawPixelMapHolder(new PixelMapHolder(pixelMap),imageStartX,imageStartY,paintIcons);
        }

        if(bitmapM2 !=null) {
            imageStartX = (0 + (fabWidth/2)) - (bitmapM2.getImageInfo().size.width);
            imageStartY = (0 + (fabHeight/2)) - (bitmapM2.getImageInfo().size.height);
            PixelMap.InitializationOptions opts = new PixelMap.InitializationOptions();
            opts.size = new Size(40,40);
            opts.pixelFormat = PixelFormat.ARGB_8888;
            PixelMap pixelMap = PixelMap.create(bitmapM2,opts);
            canvas.drawPixelMapHolder(new PixelMapHolder(pixelMap),imageStartX,imageStartY,paintIcons);
        }

        if(bitmapM3 !=null) {
            imageStartX = (fabWidth + (fabWidth/2)) - (bitmapM3.getImageInfo().size.width);
            imageStartY = (0 + (fabHeight/2)) - (bitmapM3.getImageInfo().size.height);
            PixelMap.InitializationOptions opts = new PixelMap.InitializationOptions();
            opts.size = new Size(40,40);
            opts.pixelFormat = PixelFormat.ARGB_8888;
            PixelMap pixelMap = PixelMap.create(bitmapM3,opts);
            canvas.drawPixelMapHolder(new PixelMapHolder(pixelMap),imageStartX,imageStartY,paintIcons);
        }
    }

    private void drawTopRightMenuIcons(Canvas canvas){
        int imageStartX,imageStartY;
        if(bitmapM1 !=null) {
            imageStartX = (fabWidth + (fabWidth/2)) - (bitmapM1.getImageInfo().size.width);
            imageStartY = (fabHeight + (fabWidth/2)) - (bitmapM1.getImageInfo().size.height);
            PixelMap.InitializationOptions opts = new PixelMap.InitializationOptions();
            opts.size = new Size(40,40);
            opts.pixelFormat = PixelFormat.ARGB_8888;
            PixelMap pixelMap = PixelMap.create(bitmapM1,opts);
            canvas.drawPixelMapHolder(new PixelMapHolder(pixelMap),imageStartX,imageStartY,paintIcons);
        }

        if(bitmapM2 !=null) {
            imageStartX = (fabWidth + (fabWidth/2)) - (bitmapM2.getImageInfo().size.width);
            imageStartY = (0 + (fabHeight/2)) - (bitmapM2.getImageInfo().size.height);
            PixelMap.InitializationOptions opts = new PixelMap.InitializationOptions();
            opts.size = new Size(40,40);
            opts.pixelFormat = PixelFormat.ARGB_8888;
            PixelMap pixelMap = PixelMap.create(bitmapM2,opts);
            canvas.drawPixelMapHolder(new PixelMapHolder(pixelMap),imageStartX,imageStartY,paintIcons);
        }

        if(bitmapM3 !=null) {
            imageStartX = (0 + (fabWidth/2)) - (bitmapM3.getImageInfo().size.width);
            imageStartY = (0 + (fabHeight/2)) - (bitmapM3.getImageInfo().size.height);
            PixelMap.InitializationOptions opts = new PixelMap.InitializationOptions();
            opts.size = new Size(40,40);
            opts.pixelFormat = PixelFormat.ARGB_8888;
            PixelMap pixelMap = PixelMap.create(bitmapM3,opts);
            canvas.drawPixelMapHolder(new PixelMapHolder(pixelMap),imageStartX,imageStartY,paintIcons);
        }
    }

    @Override
    public boolean onTouchEvent(Component component, TouchEvent touchEvent) {
        if (touchEvent.getAction() == TouchEvent.PRIMARY_POINT_DOWN) {

            if(menuOpenDirection.equals(TOP_LEFT))
                touch4TopLeftMenu(touchEvent);
            else
                touch4TopRightMenu(touchEvent);

            return true;
        }
        return false;
    }

    private void touch4TopLeftMenu(TouchEvent event) {
        int activePointerIndex = event.getIndex();
        MmiPoint point = event.getPointerPosition(activePointerIndex);
        if(!isOpened) {
            if(point.getX() > fabWidth && point.getY() > fabHeight) {
                isOpened = true;
                startAnimationTopLeftMenu();
                if(onMenuClickListener!=null)onMenuClickListener.onMenuOpen();
            }
        }
        else {
            // Touch of BOTTOM_RIGHT
            if(point.getX() > fabWidth && point.getY() > fabHeight) {
                closeTopLeftM();
            }
            // Touch of TOP-LEFT
            else if(point.getX() < fabWidth && point.getY() < fabHeight) {
                if(onMenuClickListener!=null)onMenuClickListener.onClickMenu2();
                if(autoClose)closeTopLeftM();
            }
            // Touch of TOP-RIGHT
            else if(point.getX() > fabWidth && point.getY() < fabHeight) {
                if(onMenuClickListener!=null)onMenuClickListener.onClickMenu3();
                if(autoClose)closeTopLeftM();
            }
            // Touch of BOTTOM-LEFT
            else if(point.getX() < fabWidth && point.getY() > fabHeight) {
                if(onMenuClickListener!=null)onMenuClickListener.onClickMenu1();
                if(autoClose)closeTopLeftM();
            }
        }
    }

    private void closeTopLeftM(){
        isOpened = false;
        resetAnimationTopLeftMenu();
        if(onMenuClickListener!=null)onMenuClickListener.onMenuClose();
    }

    private void touch4TopRightMenu(TouchEvent event) {
        int activePointerIndex = event.getIndex();
        MmiPoint point = event.getPointerPosition(activePointerIndex);
        if(!isOpened) {
            if(point.getX() < fabWidth && point.getY() > fabHeight) {
                isOpened = true;
                startAnimationTopRightMenu();
                if(onMenuClickListener!=null)onMenuClickListener.onMenuOpen();
            }
        }
        else {
            // Touch of BOTTOM_LEFT
            if(point.getX() < fabWidth && point.getY() > fabHeight) {
                closeTopRightM();
            }
            // Touch of TOP-RIGHT
            else if(point.getX() > fabWidth && point.getY() < fabHeight) {
                if(onMenuClickListener!=null)onMenuClickListener.onClickMenu2();
                if(autoClose) closeTopRightM();
            }
            // Touch of TOP-LEFT
            else if(point.getX() < fabWidth && point.getY() < fabHeight) {
                if(onMenuClickListener!=null)onMenuClickListener.onClickMenu3();
                if(autoClose) closeTopRightM();
            }
            // Touch of BOTTOM-RIGHT
            else if(point.getX() > fabWidth && point.getY() > fabHeight) {
                if(onMenuClickListener!=null)onMenuClickListener.onClickMenu1();
                if(autoClose) closeTopRightM();
            }
        }
    }

    private void closeTopRightM(){
        isOpened = false;
        resetAnimationTopRightMenu();
        if(onMenuClickListener!=null)onMenuClickListener.onMenuClose();
    }

    private void startAnimationTopRightMenu() {
        // Setup Background and Foreground Shadow
        paintFabBG = paintFabBGWithoutShadow;
        BlurDrawLooper blurDrawLooper = new BlurDrawLooper(2,0,0,Color.BLACK);
        paintFAB.setBlurDrawLooper(blurDrawLooper);

        AnimatorGroup fabsAnimSet = new AnimatorGroup();
        ValueAnimator moveTR = ValueAnimator.ofInt(0, (fabWidth/2)+MARGIN_BW_SQUARES);
        moveTR.setValueUpdateListener(new AnimatorValue.ValueUpdateListener() {
            @Override
            public void onUpdate(AnimatorValue animatorValue, float v) {
                xTransTR = (int)v;
                // To convert xTransTR value in negative
                yTransTR = xTransTR - 2*xTransTR;
                invalidate();
            }
        });

        ValueAnimator moveTL = ValueAnimator.ofInt(0, -(fabWidth/2)-MARGIN_BW_SQUARES);
        moveTL.setValueUpdateListener(new AnimatorValue.ValueUpdateListener() {
            @Override
            public void onUpdate(AnimatorValue animatorValue, float v) {
                yTransTL = (int)v;
                invalidate();
            }
        });

        ValueAnimator moveBR = ValueAnimator.ofInt(0, (fabWidth/2)+MARGIN_BW_SQUARES);
        moveBR.setValueUpdateListener(new AnimatorValue.ValueUpdateListener() {
            @Override
            public void onUpdate(AnimatorValue animatorValue, float v) {
                xTransBR = (int)v;
                invalidate();
            }
        });

        scaleFAB = ValueAnimator.ofFloat(1f, 1.9f);
        scaleFAB.setValueUpdateListener(new AnimatorValue.ValueUpdateListener() {
            @Override
            public void onUpdate(AnimatorValue animatorValue, float v) {
                factorScaleS = v;
                factorScaleP = fabWidth/2;
                invalidate();
            }
        });

        rotatePlus = ValueAnimator.ofInt(0, 45);
        rotatePlus.setValueUpdateListener(new AnimatorValue.ValueUpdateListener() {
            @Override
            public void onUpdate(AnimatorValue animatorValue, float v) {
                plusRotationDegree = (int)v;
                invalidate();
            }
        });

        alphaIcon = ValueAnimator.ofInt(0, 255);
        alphaIcon.setValueUpdateListener(new AnimatorValue.ValueUpdateListener() {
            @Override
            public void onUpdate(AnimatorValue animatorValue, float v) {
                alphaAmount = (int)v;
                paintIcons.setAlpha(alphaAmount);
                invalidate();
            }
        });
        alphaIcon.setDuration(1000);
        alphaIcon.start();

        fabsAnimSet.runParallel(moveTR, moveTL, moveBR,  scaleFAB, rotatePlus);
        fabsAnimSet.setDuration(ANIM_SPEED);
        fabsAnimSet.setCurveType(Animator.CurveType.LINEAR);
        fabsAnimSet.start();

    }

    private void resetAnimationTopRightMenu(){
        AnimatorGroup fabsAnimSet = new AnimatorGroup();
        ValueAnimator moveTR = ValueAnimator.ofInt((fabWidth/2)+MARGIN_BW_SQUARES, 0);
        moveTR.setValueUpdateListener(new AnimatorValue.ValueUpdateListener() {
            @Override
            public void onUpdate(AnimatorValue animatorValue, float v) {
                xTransTR = (int)v;
                // To convert xTransTR value in negative
                yTransTR = xTransTR - 2*xTransTR;
                invalidate();
            }
        });


        ValueAnimator moveTL = ValueAnimator.ofInt(-(fabWidth/2)-MARGIN_BW_SQUARES, 0 );
        moveTL.setValueUpdateListener(new AnimatorValue.ValueUpdateListener() {
            @Override
            public void onUpdate(AnimatorValue animatorValue, float v) {
                yTransTL = (int)v;
                invalidate();
            }
        });

        ValueAnimator moveBR = ValueAnimator.ofInt((fabWidth/2)+MARGIN_BW_SQUARES, 0);
        moveBR.setValueUpdateListener(new AnimatorValue.ValueUpdateListener() {
            @Override
            public void onUpdate(AnimatorValue animatorValue, float v) {
                xTransBR = (int)v;
                invalidate();
            }
        });

        scaleFAB.reverse();
        rotatePlus.reverse();
        alphaIcon.reverse();

        fabsAnimSet.setStateChangedListener(new Animator.StateChangedListener() {
            @Override
            public void onStart(Animator animator) {

            }

            @Override
            public void onStop(Animator animator) {

            }

            @Override
            public void onCancel(Animator animator) {

            }

            @Override
            public void onEnd(Animator animator) {
                paintFabBG = paintFabBGWidShadow;
                BlurDrawLooper blurDrawLooper = new BlurDrawLooper(0,0,0,Color.TRANSPARENT);
                paintFAB.setBlurDrawLooper(blurDrawLooper);
            }

            @Override
            public void onPause(Animator animator) {

            }

            @Override
            public void onResume(Animator animator) {

            }
        });
        fabsAnimSet.runParallel(moveTL, moveTR, moveBR);
        fabsAnimSet.setDuration(ANIM_SPEED);
        fabsAnimSet.setCurveType(Animator.CurveType.LINEAR);
        fabsAnimSet.start();
    }

    private void startAnimationTopLeftMenu() {
        // Setup Background and Foreground Shadow
        paintFabBG = paintFabBGWithoutShadow;
        BlurDrawLooper blurDrawLooper = new BlurDrawLooper(2,0,0,Color.BLACK);
        paintFAB.setBlurDrawLooper(blurDrawLooper);

        AnimatorGroup fabsAnimSet = new AnimatorGroup();
        ValueAnimator moveTL = ValueAnimator.ofInt(0, -(fabWidth/2)-MARGIN_BW_SQUARES);
        moveTL.setValueUpdateListener(new AnimatorValue.ValueUpdateListener() {
            @Override
            public void onUpdate(AnimatorValue animatorValue, float v) {
                xTransTL = yTransTL = (int)v;
                invalidate();
            }
        });

        ValueAnimator moveTR = ValueAnimator.ofInt(0, -(fabWidth/2)-MARGIN_BW_SQUARES);
        moveTR.setValueUpdateListener(new AnimatorValue.ValueUpdateListener() {
            @Override
            public void onUpdate(AnimatorValue animatorValue, float v) {
                yTransTR = (int)v;
                invalidate();
            }
        });

        ValueAnimator moveBL = ValueAnimator.ofInt(0, -(fabWidth/2)-MARGIN_BW_SQUARES);
        moveBL.setValueUpdateListener(new AnimatorValue.ValueUpdateListener() {
            @Override
            public void onUpdate(AnimatorValue animatorValue, float v) {
                xTransBL = (int)v;
                invalidate();
            }
        });

        scaleFAB = ValueAnimator.ofFloat(1f, 1.9f);
        AnimatorValue scaleFAB = new AnimatorValue();
        scaleFAB.setValueUpdateListener(new AnimatorValue.ValueUpdateListener() {
            @Override
            public void onUpdate(AnimatorValue animatorValue, float v) {
                factorScaleS = v * 1.9f;
                factorScaleP = fabWidth/2;
                invalidate();
            }
        });

        rotatePlus = ValueAnimator.ofInt(0, 45);
        rotatePlus.setValueUpdateListener(new AnimatorValue.ValueUpdateListener() {
            @Override
            public void onUpdate(AnimatorValue animatorValue, float v) {
                plusRotationDegree = (int)v;
                invalidate();
            }
        });

        alphaIcon = ValueAnimator.ofInt(0, 255);
        alphaIcon.setValueUpdateListener(new AnimatorValue.ValueUpdateListener() {
            @Override
            public void onUpdate(AnimatorValue animatorValue, float v) {
                alphaAmount = (int)v;
                paintIcons.setAlpha(alphaAmount);
                invalidate();
            }
        });
        alphaIcon.setDuration(1000);
        alphaIcon.start();
        fabsAnimSet.runParallel(moveTL, moveTR, moveBL, scaleFAB, rotatePlus);
        fabsAnimSet.setDuration(ANIM_SPEED);
        fabsAnimSet.start();
    }

    private void resetAnimationTopLeftMenu(){
        AnimatorGroup fabsAnimSet = new AnimatorGroup();
        ValueAnimator moveTL = ValueAnimator.ofInt(-(fabWidth/2)-MARGIN_BW_SQUARES, 0);
        moveTL.setValueUpdateListener(new AnimatorValue.ValueUpdateListener() {
            @Override
            public void onUpdate(AnimatorValue animatorValue, float v) {
                xTransTL = yTransTL = (int)v;
                invalidate();
            }
        });

        ValueAnimator moveTR = ValueAnimator.ofInt(-(fabWidth/2)-MARGIN_BW_SQUARES,0);
        moveTR.setValueUpdateListener(new AnimatorValue.ValueUpdateListener() {
            @Override
            public void onUpdate(AnimatorValue animatorValue, float v) {
                yTransTR = (int)v;
                invalidate();
            }
        });

        ValueAnimator moveBL = ValueAnimator.ofInt( -(fabWidth/2)-MARGIN_BW_SQUARES,0);
        moveBL.setValueUpdateListener(new AnimatorValue.ValueUpdateListener() {
            @Override
            public void onUpdate(AnimatorValue animatorValue, float v) {
                xTransBL = (int)v;
                invalidate();
            }
        });
        factorScaleP = 0.8f;
        scaleFAB.reverse();
        rotatePlus.reverse();
        alphaIcon.reverse();


        fabsAnimSet.setStateChangedListener(new Animator.StateChangedListener() {
            @Override
            public void onStart(Animator animator) {

            }

            @Override
            public void onStop(Animator animator) {

            }

            @Override
            public void onCancel(Animator animator) {

            }

            @Override
            public void onEnd(Animator animator) {
                paintFabBG = paintFabBGWidShadow;
                BlurDrawLooper blurDrawLooper = new BlurDrawLooper(0,0,0,Color.TRANSPARENT);
                paintFAB.setBlurDrawLooper(blurDrawLooper);
            }

            @Override
            public void onPause(Animator animator) {

            }

            @Override
            public void onResume(Animator animator) {

            }
        });
        fabsAnimSet.runParallel(moveTL, moveTR, moveBL);
        fabsAnimSet.setDuration(ANIM_SPEED);
        fabsAnimSet.start();
    }




    public String getMenuOpenDirection() {
        return menuOpenDirection;
    }

    /**
     * @param menuOpenDirection  "top_left" OR "top_right",  Default direction will be "top_left"
     */
    public void setMenuOpenDirection(String menuOpenDirection) {
        this.menuOpenDirection = menuOpenDirection;
    }

    /**
     * @return
     */
    public Color getFABColor() {
        return fabColor;
    }

    /**
     * @param color
     */
    public void setFABColor(Color color) {
        this.fabColor = color;
        invalidate();
        paintFAB.setColor(fabColor);
    }

    /**
     * @param autoClose true or false : default is false
     */
    public void setAutoClose(boolean autoClose) {
        this.autoClose = autoClose;
    }

    /**
     * Register callbacks to be invoked when this Menu is clicked.
     *
     * @param onMenuClickListener The callback that will run
     */
    public void setOnMenuClickListener(OnMenuClickListener onMenuClickListener) {
        this.onMenuClickListener = onMenuClickListener;
    }
}
